package sip;

import javax.sip.*;
import javax.sip.address.Address;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.header.*;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Properties;
import java.util.TooManyListenersException;

public class SipLayer
        implements SipListener {

    private MessageProcessor messageProcessor;

    private SipStack sipStack;

    private SipFactory sipFactory;
    private AddressFactory addressFactory;
    private HeaderFactory headerFactory;
    private MessageFactory messageFactory;

    private SipProvider sipProvider;

    private String username;

    /** Here we initialize the SIP stack. */
    SipLayer(String username, String ip, int port) throws PeerUnavailableException, TransportNotSupportedException,
            InvalidArgumentException, ObjectInUseException, TooManyListenersException {
        setUsername(username);
        sipFactory = SipFactory.getInstance();
        sipFactory.setPathName("gov.nist");
        Properties properties = new Properties();
        properties.setProperty("javax.sip.STACK_NAME","TextClient");
        properties.setProperty("javax.sip.IP_ADDRESS",ip);

        properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
        properties.setProperty("gov.nist.javax.sip.SERVER_LOG","textclient.txt");
        properties.setProperty("gov.nist.javax.sip.DEBUG_LOG","textclientdebug.log");

        sipStack = sipFactory.createSipStack(properties);
        headerFactory = sipFactory.createHeaderFactory();
        addressFactory = sipFactory.createAddressFactory();
        messageFactory = sipFactory.createMessageFactory();
        ListeningPoint tcp = sipStack.createListeningPoint(port, "tcp");
        ListeningPoint udp = sipStack.createListeningPoint(port, "udp");

        sipProvider = sipStack.createSipProvider(tcp);
        sipProvider.addSipListener(this);
        sipProvider = sipStack.createSipProvider(udp);
        sipProvider.addSipListener(this);
    }

    /**
     * This method is called by the SIP stack when a new request arrives.
     */
    //handle message
    public void processRequest(RequestEvent evt) {
        Request req = evt.getRequest();
        String method = req.getMethod();
        FromHeader fromHeader = (FromHeader)req.getHeader("From");
        String info = fromHeader.getAddress().getURI().toString();
        String[] str1 = info.split("@");
        // ?????
        String fromAddress = str1[1];
        // ???name
        String fromName = fromHeader.getAddress().getDisplayName();
        // ???????
        User fromUser = new User(fromName,fromAddress);

        // ????
        String message =new String(req.getRawContent());


        if (method.equals("REGISTER")){
            // ??????
            messageProcessor.processRegister(fromUser,message);
        } else if (method.equals("MESSAGE")) {
            messageProcessor.processMessage(fromUser,message);

            // ??OK
            Response response;
            try { //Reply with OK
                response = messageFactory.createResponse(200, req);
                ToHeader toHeader = (ToHeader)response.getHeader(ToHeader.NAME);
                toHeader.setTag("888"); //This is mandatory as per the spec.
                ServerTransaction st = sipProvider.getNewServerTransaction(req);
                st.sendResponse(response);
            } catch (Throwable e) {
                e.printStackTrace();
                messageProcessor.processError("Can't send OK reply.");
            }
        } else if (method.equals("CANCEL")) {
            // ????
            messageProcessor.processCancel(fromUser,message);
        } else {
            messageProcessor.processError("Bad request type: " + method);
        }
    }

    /** This method is called by the SIP stack when a response arrives. */
    public void processResponse(ResponseEvent evt) {
        Response response = evt.getResponse();
        int status = response.getStatusCode();

        if( (status >= 200) && (status < 300) ) { //Success!
            messageProcessor.processInfo("--Sent");
            return;
        }

        messageProcessor.processError("Previous message not sent: " +
                status);
    }

    /**
     * This method is called by the SIP stack when there's no answer
     * to a message. Note that this is treated differently from an error
     * message.
     */
    public void processTimeout(TimeoutEvent evt) {
        messageProcessor.processError("Previous message not sent: " +
                "timeout");
    }

    /**
     * This method uses the SIP stack to send a message.
     */
    public void sendMessage(String to, String message,String method) throws
            ParseException, InvalidArgumentException, SipException {

        SipURI from = addressFactory.createSipURI(getUsername(),
                getHost() + ":" + getPort());
        Address fromNameAddress = addressFactory.createAddress(from);
        fromNameAddress.setDisplayName(getUsername());
        FromHeader fromHeader =
                headerFactory.createFromHeader(fromNameAddress,
                        "textclientv1.0");

        String username = to.substring(to.indexOf(":")+1, to.indexOf("@"));
        String address = to.substring(to.indexOf("@")+1);

        SipURI toAddress =
                addressFactory.createSipURI(username, address);
        Address toNameAddress = addressFactory.createAddress(toAddress);
        toNameAddress.setDisplayName(username);
        ToHeader toHeader =
                headerFactory.createToHeader(toNameAddress, null);

        SipURI requestURI =
                addressFactory.createSipURI(username, address);
        requestURI.setTransportParam("udp");

        ArrayList viaHeaders = new ArrayList();
        ViaHeader viaHeader =
                headerFactory.createViaHeader(
                        getHost(),
                        getPort(),
                        "udp",
                        null);
        viaHeaders.add(viaHeader);

        CallIdHeader callIdHeader = sipProvider.getNewCallId();
        CSeqHeader cSeqHeader = null;

        String type = null;
        if(method.equals("message")){
            cSeqHeader = headerFactory.createCSeqHeader(1, Request.MESSAGE);
            type = cSeqHeader.getMethod();
        }
        else if(method.equals("register")){
            cSeqHeader = headerFactory.createCSeqHeader(1, Request.REGISTER);
            type = cSeqHeader.getMethod();
        }
        else if(method.equals("quit")){
            cSeqHeader = headerFactory.createCSeqHeader(1, Request.CANCEL);
            type = cSeqHeader.getMethod();
        }
        else
            System.out.println("error");
        //System.out.println(type);

        MaxForwardsHeader maxForwards =
                headerFactory.createMaxForwardsHeader(70);

        Request request =
                messageFactory.createRequest(
                        requestURI,
                        type,
                        callIdHeader,
                        cSeqHeader,
                        fromHeader,
                        toHeader,
                        viaHeaders,
                        maxForwards);


        SipURI contactURI = addressFactory.createSipURI(getUsername(),
                getHost());
        contactURI.setPort(getPort());
        Address contactAddress = addressFactory.createAddress(contactURI);
        contactAddress.setDisplayName(getUsername());
        ContactHeader contactHeader =
                headerFactory.createContactHeader(contactAddress);
        request.addHeader(contactHeader);

        ContentTypeHeader contentTypeHeader =
                headerFactory.createContentTypeHeader("text", "html");
        request.setContent(message, contentTypeHeader);

        sipProvider.sendRequest(request);
    }




    public String getHost() {
        //int port = sipProvider.getListeningPoint().getPort();
        String host = sipStack.getIPAddress();
        return host;
    }

    public int getPort() {
        int port = sipProvider.getListeningPoint().getPort();
        return port;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String newUsername) {
        username = newUsername;
    }

    public MessageProcessor getMessageProcessor() {
        return messageProcessor;
    }

    public void setMessageProcessor(MessageProcessor newMessageProcessor) {
        messageProcessor = newMessageProcessor;
    }

}
